查看原文
其他

当Jetpack Compose 遇到 Navigation

fundroid AndroidPub 2022-05-15

很多Android项目使用 Jetpack Navigation进行页面切换。Navigation在设计上高度抽象,只负责导航逻辑不关心页面的具体实现,无论是Activity、Fragment或是已定义View都可以通过Navigation实现导航。

Jetpack Compose作为一个声明式UI框架经常拿来与React 、Flutter等作对比,但是遗憾的是一直缺少同类框架的导航机制,如今通过Jetpack自家的Navigation 也终于可以补齐Compose的短板了。

Installation

使用navigation-compose,只需要在build.gradle中添加依赖:

implementation "androidx.navigation:navigation-compose:1.0.0-alpha02"

NavController

Navigation中我们通过findNavController扩展方法获取NavController,然后进行跳转。NavController中管理NavGraph等配置信息,所以是stateful的,在Compose的纯函数中需要通过以下方式获取一个有状态的实例

val navController = rememberNavController()

NavHost

NavHost是NavController的持有者,NavHostFragment是Fragment对于NavHost的实现。Compose基于composable函数渲染UI,没有Fragment这样的具体实例做载体,所以Compose的NavHost更加抽象,你可以将其理解为一个容器,内部通过NavController在“页面切换”时,渲染当前UI

val navController = rememberNavController()
NavHost(
        navController = navController,
        startDestination = "first_screen"
) {
    composable("first_screen") {
        // first screen
    }
    composable("second_screen") {
        // second screen
    }
}

如上,NavHost接受两个参数,navController和startDestination,这是Navigation的标准用法,不再赘述。其DSL内部的composable用来声明各个页面

A Navigation Sample

Compose中一个完整的Navigation定义如下:

@Composable
fun ComposeNavigation() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "first_screen"
    ) {
        composable("first_screen") {
            FirstScreen(navController = navController)
        }
        composable("second_screen") {
            SecondScreen(navController = navController)
        }
        composable("third_screen") {
            ThirdScreen(navController = navController)
        }
    }
}

配置了三个页面,初始页面是first_screencomposable()的参数作为Destination的id,用于后续跳转

@Composable
fun FirstScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "First Screen\n" +
                    "Click me to go to Second Screen",
            color = Color.Green,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.padding(24.dp).clickable(onClick = {
                // this will navigate to second screen
                navController.navigate("second_screen")
            })
        )
    }
}

如上,FirstScreen中,通过navController.navigate("second_screen")跳转到SecondScreen。

同样的, 其他的页面定义如下:

@Composable
fun SecondScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Second Screen\n" +
                    "Click me to go to Third Screen",
            color = Color.Yellow,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.clickable(onClick = {
                // this will navigate to third screen
                navController.navigate("third_screen")
            })
        )
    }
}

@Composable
fun ThirdScreen(navController: NavController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Third Screen\n" +
                    "Click me to go to First Screen",
            color = Color.Red,
            style = TextStyle(textAlign = TextAlign.Center),
            modifier = Modifier.clickable(onClick = {
                // this will navigate to first screen
                navController.navigate("first_screen")
            })
        )
    }
}

最后,需要在setContent中调用ComposeNavigation

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        ComposeNavigationTheme {
            ComposeNavigation()
        }
    }
}

之后,就可以在Compose项目中进行页面切换了,而且还支持BackStack的回退。

最后

以前如果想使用Compose实现多页面的APP,只能在Fragment或者Activity内部写Compose代码。现在有了Navigation,可以彻底摆脱Fragment或者Activity了,这得益于Navigation高度抽象的设计,有兴趣的同学可以阅读NavController源码了解其中细节。



推荐阅读:


引入Jetpack后,App会发生哪些变化?


Jetpack Compose for Desktop 初体验


小心RxJava操作符中暗藏的危机


MotionLayout实现无限轮播图效果



↓关注公众号↓↓添加微信交流↓





您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存